<?php
/***************************************************************************
 *   Copyright (C) 2007 by Andrea Blankenstijn, David Dieulivol            *
 *   darkan9el@gmail.com, daviddieulivol5@hotmail.com                      *
 *                                                                         *
 *   All rights reserved.                                                  *
 *   This work is released under the terms of the New BSD License.         *
 *   See LICENSE.BSD file for licensing details                            *
 ***************************************************************************/
/* * *
 * $Id: rssFeed.class.php 47 2007-08-19 13:03:28Z DarkAn9el $
 * * */

/**
 * \class RssFeed
 * \version $Revision: 47 $
 * \date $Date: 2007-08-19 13:03:28 +0000 (Sun, 19 Aug 2007) $
 * \author Andrea Blankenstijn
 * \brief Classe de génération de flux RSS 2.0
 * 
 * Cette classe permet de générer un flux RSS 2.0 en utilisant l'extension simpleXML.
 * Du fait d'une limitation de simpleXML, une fois un élément est ses attributs créés,
 * ils ne peuvents plus être supprimés ou modifiés.
 * Tout les éléments de la normes RSS 2.0, ormis docs, cloud, ttl, rating, textInput,
 * skipHours et skipDays, sont implémentés.
 */
 
 class RssFeed
 {
 	/**
	 * \brief Génère une date au format requis par la norme RSS à partir d'un timestamp UNIX
	 * \param[in] $timestamp timestamp UNIX de la date
	 * \return date RFC-822 avec l'année sur 4 chiffres 
	 */
 	public static function makeRFC822Date($timestamp = NULL)
 	{
 		if(empty($timestamp))
 			$timestamp = time();

 		return date('D, d M Y H:i:s O', $timestamp);
 	}

	public static function sendMimeType()
	{
		header('Content-type: application/rss+xml');
	}

 	private $rss; ///< Objet simpleXML représentant le flux RSS

 	// variables définissant la présence de certains éléments
 	private $hasCategory, $hasCopyright, $hasEditor, $hasImage, $hasLang, $hasLastBuildDate, $hasPubDate; 
 	
 	/**
 	 * \brief Constructeur de la classe générant le code RSS
 	 * \param[in] $channelTitle titre du flux RSS
 	 * \param[in] $websiteUrl lien du site du flux
 	 * \param[in] $channelDescription descriptif du flux
 	 * \param[in] $itemArray tableau des informations des éléments item du flux
 	 * \see addItemNode
 	 */
 	public function __construct($channelTitle, $websiteUrl, $channelDescription, $itemArray = NULL)
 	{
 		$xmlString = '<?xml version="1.0" encoding="UTF-8"?>'.
		             '<rss version="2.0">'.
		             '<channel>'.
		             '</channel>'.
		             '</rss>';

		$this->rss = new SimpleXMLElement($xmlString);

		// Ajout des éléments de channel obligatoires
		$this->rss->channel->addChild('title', htmlspecialchars($channelTitle));
		$this->rss->channel->addChild('link', htmlspecialchars($websiteUrl));
		$this->rss->channel->addChild('description', htmlspecialchars($channelDescription));
		$this->rss->channel->addChild('generator', 'DaяkAn9el &amp; Snakedown\'s RssFeed PHP Class');

		// Ajout des éléments item
		if(is_array($itemArray))
		{
			if(is_array($itemArray[0]))
				$this->addItemNodes($itemArray);
			else
				$this->addItemNode($itemArray);
		}
 	}

	/**
	 * \brief Ajoute un nœud item et ses sous-éléments avec les valeurs spécifiées
	 * \param[in] $itemValues tableau contenenant les valeurs des différents éléments
	 * facultatif de item.
	 * chaque indice peut contenir une valeur nulle, dans ce cas l'élément ou l'attribut
	 * associé sera ignoré.
	 *   - [0]  → title (string)
	 *   - [1]  → link (url)
	 *   - [2]  → description
	 *   - [3]  → author (string)
	 *   - [4]  → category (string)
	 *   - [5]  → comments (url)
	 *   - [6]  → enclosure url (url)
	 *   - [7]  → enclosure lenght (bytes)
	 *   - [8]  → enclosure type (mimetype)
	 *   - [9]  → guid (identifiant unique)
	 *   - [10] → guid permaLink (bool)
	 *   - [11] → pubDate (date RFC-822)
	 * \see addItemNodes
	 * \see makeRFC822Date
	 */
	public function addItemNode($itemValues)
	{
		// les différents éléments de title
		@$title       = $itemValues[0];
		@$link        = $itemValues[1]; 
		@$description = $itemValues[2];
		@$author      = $itemValues[3];
		@$category    = $itemValues[4];
		@$comments    = $itemValues[5];
		@$enclUrl     = $itemValues[6];  // attribut url de l'élément enclosure
		@$enclLenght  = $itemValues[7];  // attribut lenght de l'élément enclosure
		@$enclType    = $itemValues[8];  // attribut type de l'élément enclosure
		@$guid        = $itemValues[9];
		@$isPermaLink = $itemValues[10]; // défini l'attribut isPermaLink de l'élément guid
		@$pubDate     = $itemValues[11];
	
		// au moins un de ces deux éléments est obligatoire
		if(empty($title) AND empty($description))
			throw new Exception('Au moins un des éléments title ou description doit être défini', 1);
	
		$item = $this->rss->channel->addChild('item');
	
		if(!empty($title))
			$item->addChild('title', htmlspecialchars($title));

		if(!empty($link))
			$item->addChild('link', $link);

		if(!empty($description))
			$item->addChild('description', htmlspecialchars($description));

		if(!empty($author))
			$item->addChild('author', htmlspecialchars($author));

		if(!empty($category))
			$item->addChild('category', htmlspecialchars($category));

		if(!empty($comments))
			$item->addChild('comments', $comments);

		if(!empty($enclUrl) AND !empty($enclLenght) AND !empty($enclType))
		{
			$item->addChild('enclosure');
			$item->enclosure->addAttribute('url', $enclUrl);
			$item->enclosure->addAttribute('lenght', $enclLenght);
			$item->enclosure->addAttribute('type', $enclType);
		}

		if(!empty($guid))
		{
			$item->addChild('guid', $guid);
			if($isPermaLink)
				$item->guid->addAttribute('isPermaLink', 'true');
				
		}
		if(!empty($pubDate))
			$item->addChild('pubDate', $pubDate);
	}
	
	/**
	 * \brief Ajoute plusieurs nœuds item et ses sous-éléments avec les valeurs spécifiées
	 * \param[in] $itemArray tableau dont chaque indice contient un tableau tel que décrit
	 *            pour addItemNode.
	 * \see addItemNode
	 */
	public function addItemNodes($itemArray)
	{
		foreach($itemArray as $item)
			$this->addItemNode($item);
	}

 	/**
 	 * \brief retourne le flux RSS sous forme de chaîne
 	 * \param[in] $formatOutput si vrai utilise DOMDocument formater la sortie
 	 * \return $xmlOut données XML du flux
 	 */
 	public function getRenderedXml($formatOutput = false)
 	{
 		if($formatOutput)
 		{
 			$dom               = new DOMDocument('1.0', 'UTF-8');
 			$dom->formatOutput = true;

 			$domnode = dom_import_simplexml($this->rss);
 			$domnode = $dom->importNode($domnode, true);
 			$dom->appendChild($domnode);

 			$xmlOut  = $dom->saveXML();
 		}
 		else
 			$xmlOut = $this->rss->asXML();

 		return $xmlOut;
 	}

	/**
	 * \brief Ajoute l'élément facultatif category de channel
	 * \param[in] $category la valeur donnée à l'élément (string)
	 */
	public function setChannelCategory($category)
	{
		if(empty($this->hasCategory))
		{
			$this->rss->channel->addChild('category', htmlspecialchars($category));
			$this->hasCategory = TRUE;
		}
	}


	/**
	 * \brief Ajoute l'élément facultatif category de copyright
	 * \param[in] $copyright la valeur donnée à l'élément (string)
	 */
	public function setChannelCopyright($copyright)
	{
		if(empty($this->hasCopyright))
		{
			$this->rss->channel->addChild('copyright', htmlspecialchars($copyright));
			$this->hasCopyright = TRUE;
		}
	}


	/**
	 * \brief Ajoute l'élément facultatif image de channel
	 * \param[in] $image la valeur donnée à l'élément (url)
	 */
	public function setChannelImage($image)
	{
		if(empty($this->hasImage))
		{
			$this->rss->channel->addChild('image', htmlspecialchars($image));
			$this->hasImage = TRUE;
		}
	}


	/**
	 * \brief Ajoute l'élément facultatif language de channel
	 * \param[in] $lang la valeur donnée à l'élément (string)
	 */
	public function setChannelLang($lang)
	{
		if(empty($this->hasLang))
		{
			$this->rss->channel->addChild('language', $lang);
			$this->hasLang = TRUE;
		}
	}


	/**
	 * \brief Ajoute l'élément facultatif lastBuildDate de channel
	 * \param[in] $date la valeur donnée à l'élément (date RFC-822)
	 * \see makeRFC822Date
	 */
	public function setChannelLastBuildDate($date)
	{
		if(empty($this->hasLastBuildDate))
		{
			$this->rss->channel->addChild('lastBuildDate', $date);
			$this->hasLastBuildDate = TRUE;
		}
	}


	/**
	 * \brief Ajoute l'élément facultatif managingEditor de channel
	 * \param[in] $editor la valeur donnée à l'élément (string)
	 */
	public function setChannelManagingEditor($editor)
	{
		if(empty($this->hasEditor))
		{
			$this->rss->channel->addChild('managingEditor', htmlspecialchars($editor));
			$this->hasEditor = TRUE;
		}
	}


	/**
	 * \brief Ajoute l'élément facultatif pubDate de channel
	 * \param[in] $date la valeur donnée à l'élément (date RFC-822)
	 * \see makeRFC822Date
	 */
	public function setChannelPubDate($date)
	{
		if(empty($this->hasPubDate))
		{
			$this->rss->channel->addChild('pubDate', $date);
			$this->hasPubDate = TRUE;
		}
	}


	/**
	 * \brief Ajoute les éléments facultatifs de channel
	 *
	 * Si la valeur d'un paramètre est nulle, l'élément associé sera
	 * ignoré.
	 *
	 * \param[in] $category valeur de category (string)
	 * \param[in] $copyright valeur de copyright (string)
	 * \param[in] $image valeur de image (url)
	 * \param[in] $lang valeur de language (string)
	 * \param[in] $lastBuildDate valeur de lastBuildDate (date RFC-822)
	 * \param[in] $editor valeur de managingEditor (string)
	 * \param[in] $pubDate valeur de pubDate (date RFC-822)
	 * \param[in] $webmaster valeur de webMaster (string)
	 * \see makeRFC822Date
	 */
	public function setChannelSubElements($category = NULL, $copyright = NULL, $image = NULL,
	                                      $lang = NULL, $lastBuildDate = NULL, $editor = NULL,
	                                      $pubDate = NULL, $webmaster = NULL)
	{
		if(!empty($category))
			$this->setChannelCategory($category);
		if(!empty($copyright))
			$this->setChannelCopyright($copyright);
		if(!empty($image))
			$this->setChannelImage($image);
		if(!empty($lang))
			$this->setChannelLang($lang);
		if(!empty($lastBuildDate))
			$this->setChannelLastBuildDate($lastBuildDate);
		if(!empty($editor))
			$this->setChannelManagingEditor($editor);
		if(!empty($pubDate))
			$this->setChannelPubDate($pubDate);
		if(!empty($webmaster))
			$this->setChannelWebmaster($webmaster);
	}


	/**
	 * \brief Ajoute l'élément facultatif webMaster de channel
	 * \param[in] $webmaster la valeur donnée à l'élément
	 */
	public function setChannelWebmaster($webmaster)
	{
		if(empty($this->webmaster))
		{
			$this->rss->channel->addChild('webMaster', htmlspecialchars($webmaster));
			$this->webmaster = TRUE;
		}
	}
 }
?>
